import {
    DefaultStyleManager,
    DefaultLayoutManager,
    DefaultComponentManager
} from "./base-managers.js";
import {ControlFactory} from "./control-factory.js";

/**
 * 插件抽象基类 (所有插件必须继承)
 */
export class BasePlugin {
    // ╔═══════════════════════════════════════════════════════════════════════════╗
    //                                   全局变量
    // ╚═══════════════════════════════════════════════════════════════════════════╝
    static DEFAULT_CONFIG = { // 必须由子类覆盖
        DEBUG: false,
        LOG_PREFIX: '[Preview] ',
        EXTENSION_NAME: 'base',
        SELECTOR: "widget-preview-area",
        HINT_MESSAGES: [
            "✨ 试试调整参数，发现更多可能性~",
            "🎨 创意需要改变，调整滑块试试？",
            "🔄 同样的参数看腻了，来点新花样？",
            "🧩 微调参数可能会有意外惊喜哦",
            "⚡ 改变一下，发现不同视觉效果"
        ],
        WIDGET_CONFIG: {
            Dropdown: { valueKey: 'value', selector: 'select' },
            Text: { valueKey: 'value', selector: 'input' },
            Textarea: { valueKey: 'value', selector: 'textarea' },
            ColorPicker: { valueKey: 'value', selector: 'input[type="color"]' },
            IntSlider: { valueKey: 'textContent', selector: '.widget-readout' },
            Checkbox: { valueKey: 'checked', selector: 'input[type="checkbox"]' },
        },
        MANAGERS: {
            style: DefaultStyleManager,
            layout: DefaultLayoutManager,
            component: DefaultComponentManager
        },
    };

    constructor(userConfig = {}) {
        if (new.target === BasePlugin) {
            throw new Error("Cannot instantiate abstract class!");
        }

        // 合并配置
        this.config = {
            ...this.constructor.DEFAULT_CONFIG,
            ...userConfig
        };
        this.config.CONSTANTS.PREVIEW_OUTPUT = `${this.config.CONSTANTS.CLASS_PREFIX}-output`;
        this.constants = this.config.CONSTANTS ?? {};

        this.$container = $(`.${this.config.SELECTOR}`);

        // 状态管理
        this.state = {
            // 配置是否更新
            configUpdated: false,
            // 上次配置是否更新
            lastConfigUpdated: false,
            // 记录连续默认参数次数
            counter: 0
        };
        this.controls = {};
        this.managers = {};
        this.events = {}
        this.defaultColorUpdateType  = { key: '', type: 'color'};
        this.colorUpdateType  = this.defaultColorUpdateType;

        // 初始化
        this._initCore();
    }

    // ╔═══════════════════════════════════════════════════════════════════════════╗
    //                                   生命周期
    // ╚═══════════════════════════════════════════════════════════════════════════╝
    _initCore() {
        this.initialized = false;
        this._initLogger();
        this._validateConfig();
        this._initManagers();
        this._registerManagers();
        this.initControls();
        this.initDom();
        this.initEvents();
        this.onMount();
        this.initialized = true;
        this._log('Plugin initialized');
        this._log('Event:', this.events);
        this._log('Config:', this.config);
        this._log('Managers:', this.managers);
        this._log('Controls:', this.controls);
    }

    /**
     * 初始化日志记录器
     */
    _initLogger() {
        this._log = (msg, data = null, level = 'info') => {
            if (!this.config.DEBUG) return;
            console[level](`${this.config.LOG_PREFIX}${msg}`, data);
        };
        /**
         * 日志记录
         * @param {string} message 日志信息
         * @param {*} data 额外数据
         * @param {string} level 日志级别
         * @param {string} prefix 日志前缀
         */
        this._log = (message, data = null, level = 'info', prefix = this.config.LOG_PREFIX) => {
            if (!this.config.DEBUG) return;

            if (prefix.length > 0 && this.config.EXTENSION_NAME.length > 0) {
                prefix += `[${this.config.EXTENSION_NAME}] `;
            }
            console[level](prefix + message, data || '');
        }
    }

    /**
     * 验证配置
     */
    _validateConfig() {
        if (!this.config.SELECTOR) throw new Error('Missing required config: SELECTOR');
        const $container = $(`.${this.config.SELECTOR}`);
        if ($container.length === 0) {
            throw new Error(`Container element not found: .${this.config.SELECTOR}`);
        }
        const $output = $(`.${this.constants.PREVIEW_OUTPUT}`);
        if ($output.length === 0) {
            throw new Error(`Output element not found: .${this.constants.PREVIEW_OUTPUT}`);
        }
    }

    /**
     * 初始化管理器
     */
    _initManagers() {
        const {style, layout, component} = this.config.MANAGERS;
        this.managers = {
            style: new style(this),
            layout: new layout(this),
            component: new component(this)
        };
    }

    /**
     * 注册管理器
     */
    _registerManagers() {
        // 注册样式
        this.managers.style?.registerStyles();
        // 初始化布局
        this.managers.layout?.initLayout();
        // 注册组件
        this.managers.component?.registerComponents();
    }

    /**
     * 初始化 DOM
     */
    initDom() {
        this.update(true);
    }

    /**
     * 初始化控件
     */
    initControls() {
        this.controls = ControlFactory.create(this);
    }

    /**
     * 初始化事件
     */
    initEvents() {
        const controls = Object.entries(this.controls);
        if (controls.length === 0) {
            this._log(`组件未初始化，无法设置事件监听`, null, 'error', this.constants.LOG_PREFIX);
        } else {
            // 控件变化监听
            controls.forEach(([key, control]) => {
                if (control.shouldListen === true && control.element.length > 0) {
                    const event = (control.event || this.constants.CONTROLS_CONFIG[key]?.event || []).join(' ');
                    if (event.length > 0) {
                        this.updateEventRecord(key, event);
                        // TODO: ipywidgets 创建的 select 组件尝试多种方法后没有实现监听上下键来实时预览
                        $(control.element).on(event, (event) => {
                            this.setColorUpdateType(event);
                            this.update(false)
                        });
                    }
                }
                // 滑块变化监听
                if (control.widget === 'IntSlider' && control.element.length > 0) {
                    // noinspection JSUnresolvedReference
                    if (key.endsWith('Target') && control.element[0].noUiSlider) {
                        this.updateEventRecord(key, 'update');
                        // noinspection JSUnresolvedReference
                        control.element[0].noUiSlider.on('update', () => {
                            if (this.initialized === true) this.update(false);
                        });
                    }
                }
            });
            this.afterInitEvents();
        }

        this.updateEventRecord('create-config-btn', 'click');
        this.updateEventRecord('reset-btn', 'click');
        // 按钮监听
        this.$container.on('click', '.create-config-btn', (e) => {
            e.preventDefault();
            this.updateOutputContent(false);
        });

        this.$container.on('click', '.reset-btn', (e) => {
            e.preventDefault();
            // 更新指令输出
            this.updateOutputContent(true);
            // 重置计数器
            this.setState({counter: 0});
            // 重置控件值
            this.resetControls();
            // 更新控件样式
            this.update(true);
            this.afterResetBtn();
        });
    }

    // ╔═══════════════════════════════════════════════════════════════════════════╗
    //                                   抽象方法
    // ╚═══════════════════════════════════════════════════════════════════════════╝

    /**
     * 插件挂载后调用
     */
    onMount() {}

    /**
     * 重置按钮点击后调用
     */
    afterResetBtn() {}

    /**
     * 初始化事件后调用
     */
    afterInitEvents() {}

    /**
     * 更新前调用
     */
    beforeUpdate() {}

    /**
     * 更新后调用
     */
    afterUpdate() {}

    /**
     * 获取配置前调用
     * @returns {Object} 配置对象
     */
    beforeGetConfig() { return {}; }

    /**
     * 获取配置后调用
     * @returns {Object} 配置对象
     */
    afterGetConfig(config, from) { return config || {}; }

    // ╔═══════════════════════════════════════════════════════════════════════════╗
    //                                   公共方法
    // ╚═══════════════════════════════════════════════════════════════════════════╝
    /**
     * 更新布局
     * @param {boolean} reset 是否重置
     */
    update(reset) {
        this.beforeUpdate();
        if (this.managers.layout?.updateLayout) {
            this.managers.layout.updateLayout(reset);
        }
        this.afterUpdate();
    }

    /**
     * 更新监听事件记录
     * @param key
     * @param event
     */
    updateEventRecord(key, event) {
        if (typeof key !== 'string' || !key.trim()) {
            throw new Error('Invalid key: must be a non-empty string');
        }

        if (typeof event !== 'string') {
            throw new Error('Event must be a string');
        }

        // 处理事件字符串
        const newEvents = event.trim().split(/\s+/).filter(Boolean);

        // 合并并去重
        this.events[key] = [
            ...new Set([
                ...(this.events[key] || []),
                ...newEvents
            ])
        ];
    }

    /**
     * 状态管理
     * @param {string} key 状态键名
     */
    getState(key) {
        return this.state[key];
    }

    /**
     * 状态管理
     * @param {Object} updates 状态更新对象
     */
    setState(updates) {
        Object.assign(this.state, updates);
    }

    /**
     * 初始化 Bootstrap Tooltip
     * @param {HTMLElement} element 要初始化的元素
     */
    initTooltip(element) {
        const $element = $(element);               // 包装为 jQuery 对象
        if (!$element.data('bs.tooltip')) {  // 检查是否已初始化
            $element.tooltip('dispose');           // 先销毁旧的实例
            $element.tooltip();                    // 调用 jQuery 的 tooltip() 完成初始化
        }
    }

    /**
     * 显示提示信息
     * @param {string} message 提示消息
     * @param {string} level 提示级别
     * @param {string} title 提示标题
     * @param {string} position 提示位置
     */
    showToast(message, level = 'success', position = 'bottom-right', title = '系统提示') {
        // 位置配置映射
        const POSITIONS = {
            'top-left': { class: 'top-0 start-0', style: '' },
            'top-center': { class: 'top-0 start-50 translate-middle-x', style: '' },
            'top-right': { class: 'top-0 end-0', style: '' },
            'bottom-left': { class: 'bottom-0 start-0', style: '' },
            'bottom-center': { class: 'bottom-0 start-50 translate-middle-x', style: 'transform: translateX(-50%)' },
            'bottom-right': { class: 'bottom-0 end-0', style: '' }
        };

        // 获取或创建容器
        let $toastContainer = $('#toast-container');
        if ($toastContainer.length === 0) {
            $toastContainer = $('<div>')
                .attr('id', 'toast-container')
                .addClass(`position-fixed ${POSITIONS[position].class}`)
                .css({
                    'z-index': '9999',
                    ...(POSITIONS[position].style && { 'transform': POSITIONS[position].style })
                })
                .appendTo('body');
        }

        // 创建 Toast
        const toastId = 'toast-' + Date.now();
        const $toast = $(`
            <div id="${toastId}" class="toast" role="alert" aria-live="assertive" aria-atomic="true">
                <div class="toast-header bg-${level} text-white">
                    <strong class="me-auto">${title}</strong>
                    <button type="button" class="btn-close btn-close-white" data-bs-dismiss="toast"></button>
                </div>
                <div class="toast-body">
                    ${message}
                </div>
            </div>
        `);

        // 添加到容器并显示
        $toastContainer.append($toast);
        // noinspection JSUnresolvedReference
        const toast = new bootstrap.Toast($toast[0], {
            autohide: true,
            delay: 3000
        });
        toast.show();

        // 自动移除
        $toast.on('hidden.bs.toast', function() {
            $(this).remove();
            // 如果容器为空则移除容器
            if ($toastContainer.children().length === 0) {
                $toastContainer.remove();
            }
        });
    }

    /**
     * 将字符串样式转换为对象
     * @param styleString
     * @returns {{}}
     */
    parseStyleString(styleString) {
        const styleObject = {};
        if (!styleString) return styleObject;

        styleString.split(';').forEach(rule => {
            const [property, value] = rule.split(':').map(s => s.trim());
            if (property && value) {
                // 转换 CSS 属性名为驼峰式（如 font-size -> fontSize）
                const jsProperty = property.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
                styleObject[jsProperty] = value;
            }
        });
        return styleObject;
    }

    /**
     * 多次未更新参数时显示提示信息
     */
    handleHints() {
        const $tag = $(`.${this.constants.PREVIEW_OUTPUT} .admonition-title .sd-badge`);
        if (this.getState('configUpdated')) {
            this.setState({counter: 0});
            return;
        }

        // 移除现有提示
        $(`.${this.constants.PREVIEW_OUTPUT} .config-hint`).remove();

        let counter = this.getState('counter');
        counter++;
        this.setState({counter: counter});
        // 当连续3次未更新时添加提示
        if (this.getState('counter') >= 3) {
            const randomMsg = this.config.HINT_MESSAGES[Math.floor(Math.random() * this.config.HINT_MESSAGES.length)];

            const hintEl = document.createElement('span');
            hintEl.className = 'config-hint';
            hintEl.innerHTML = `<span class="hint-text">${randomMsg}</span>`;
            $tag.after(hintEl);

            // 5秒后淡出提示
            setTimeout(() => {
                setTimeout(() => hintEl.remove(), 300);
            }, 5000);
        }
    }

    /**
     * 比较两个数值字符串是否相等
     * @param {string} a 字符串A
     * @param {string} b 字符串B
     * @returns {boolean} 是否相等
     */
    isNumericEqual = (a, b) => {
        try {
            return parseFloat(a) === parseFloat(b);
        } catch {
            return false;
        }
    };

    /**
     * 更新代码块
     * @param {boolean} isDefault 是否默认值
     */
    updateCodeValues(isDefault) {
        const ref = this;
        this.setState({configUpdated: false});
        const $highlight = $(`.${this.constants.PREVIEW_OUTPUT} .highlight pre`);

        if (!$highlight) return;

        // 获取所有配置值
        const config = this.getConfig(isDefault, "output");

        // 更新所有 span 内容
        $highlight.find('span.sd').each(function () {
            const $span = $(this);
            const line = $span.text().trim();
            // 只更新:开头的行
            if (line.startsWith(':')) {
                const key = line.split(':')[1].trim();
                if (key in config) {
                    const value = config[key];
                    const defaultValue = ref.constants.CONTROLS_CONFIG[key].default;
                    const CurrentData = `:${key}: ${value}\n`;
                    $span.removeClass('changed');
                    if (typeof value !== typeof defaultValue && String(value) !== String(defaultValue) && !ref.isNumericEqual(value, defaultValue)) {
                        // 数据更新时增加标识类名 changed
                        $span.addClass('changed');
                        ref.setState({configUpdated: true});
                    } else if (typeof value === typeof defaultValue && value !== defaultValue) {
                        $span.addClass('changed');
                        ref.setState({configUpdated: true});
                    }
                    $span.text(CurrentData);
                }
            }
        });
    }

    /**
     * 更新输出内容
     * @param {boolean} isDefault 是否默认值
     */
    updateOutputContent(isDefault) {
        const $time = $(`.${this.constants.PREVIEW_OUTPUT} .admonition-title .pasted-text`);
        const $tag = $(`.${this.constants.PREVIEW_OUTPUT} .admonition-title .sd-badge`);

        $tag[0].style.setProperty('color', '#ffffff', 'important');

        // 更新时间
        $time.text(new Date().toLocaleString('zh-CN', {
            year: 'numeric', month: '2-digit', day: '2-digit',
            hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false
        }).replace(/\//g, '-'));

        // 更新代码内容
        this.updateCodeValues(isDefault);

        // 更新标签状态
        if (isDefault || !this.getState('configUpdated')) {
            $tag.addClass("text-white sd-bg-success sd-bg-text-success")
                .removeClass('sd-bg-warning sd-bg-text-warning')
                .text("默认");
        } else {
            $tag.addClass("sd-bg-warning sd-bg-text-warning")
                .removeClass('sd-bg-success sd-bg-text-success')
                .text("更新");
            this.showToast('配置生成成功！');
        }

        this.setState({lastConfigUpdated: this.getState('configUpdated')})

        // 提示逻辑
        this.handleHints();
    }

    /**
     * 设置颜色更新类型
     * @param {Event} event 事件对象
     */
    setColorUpdateType(event) {
        let colorPickerClass = '';
        let colorPickerKey = '';

        const $target = $(event.target);
        const type = $target.attr('type');

        if (type !== 'text') return;

        const colorPickerElement = $target.closest('.widget-colorpicker')

        if (!colorPickerElement.length) return;

        // 获取所有类名
        const classNames = colorPickerElement.attr('class').split(/\s+/);
        // 查找用来匹配组件的类名
        colorPickerClass = classNames.find(className => className.startsWith(`${this.constants.CLASS_PREFIX}-`));
        colorPickerKey = colorPickerClass.replace(`${this.constants.CLASS_PREFIX}-`, '');

        if (colorPickerKey.length === 0) return;

        this.colorUpdateType = { key: colorPickerKey, type: 'text'};
    }

    /**
     * 获取当前配置
     * @param {boolean} returnDefault 是否只返回默认值
     * @param {string} from 调用来源
     * @returns {Object} 配置对象
     */
    getConfig(returnDefault = false, from) {
        let config;

        config = this.beforeGetConfig();

        for (const [key, spec] of Object.entries(this.constants.CONTROLS_CONFIG)) {
            // 选择只返回默认值，跳过后续逻辑
            if (returnDefault === true) {
                config[key] = spec.default;
                continue;
            }

            // 如果组件没有定义控件类型，跳过
            if (!spec.widget) {
                console.warn(`DEFAULT_CONFIG 中'${key}'未定义控件类型'`);
                continue;
            }

            const valueKey = this.config.WIDGET_CONFIG[spec.widget]["valueKey"];
            // 如果组件定义的控件类型不在已知的空间类型中，跳过
            if (!valueKey) {
                console.warn(`DEFAULT_CONFIG 中'${key}'存在 WIDGET_CONFIG 未定义的控件类型: ${spec.widget}`);
                continue;
            }


            let controlValue = this.controls[key]["getValue"]();
            if (spec.widget === 'ColorPicker' && this.colorUpdateType.key === key && this.colorUpdateType.type === 'text') {
                controlValue = this.controls[`${key}Text`]["getValue"]()
                // 重置颜色更新类型
                this.colorUpdateType = this.defaultColorUpdateType;
            }

            config[key] = controlValue ? controlValue : spec.default;
        }

        config = this.afterGetConfig(config, from);

        return config;
    }

    /**
     * 重置所有控件的值
     */
    resetControls() {
        Object.values(this.controls).forEach(c => c.reset?.());
    }
}